# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *

# 来源： https://bbs.myquant.cn/thread/2457

# 策略中必须有init方法
def init(context):
    # 每月的第一个交易日的09:40:00执行策略algo
    schedule(schedule_func=algo, date_rule='1m', time_rule='9:40:00')
    # 设置买入股票资金比例
    context.ratio = 0.8
    # 设置股票池的数量
    context.num = 30
    # 设置预期年增长率
    context.rate = 15
    # 通过get_instruments获取所有的上市股票代码，剔除停牌股和st股
    context.stock = get_instruments(exchanges='SHSE, SZSE', sec_types=SEC_TYPE_STOCK, skip_suspended=True,
                                    skip_st=True, fields='symbol, delisted_date', df=True)


def algo(context):
    # 获取当前时间
    now = context.now.strftime("%Y-%m-%d %H:%M:%S")

    # 剔除退市股和B股
    stocks = context.stock[(context.stock['delisted_date'] > now) & (context.stock['symbol'].str[5] != '9')
                           & (context.stock['symbol'].str[5] != '2')]['symbol'].to_list()

    # 获取基本每股收益
    fund1 = get_fundamentals(table='income_statement', symbols=stocks, start_date=context.now.date(),
                             end_date=context.now.date(), fields='BASICEPS', df=True)
    fund1.index = fund1.symbol

    # 获取总市值
    fund2 = get_fundamentals(table='trading_derivative_indicator', symbols=stocks,
                             start_date=context.now.date(), end_date=context.now.date(),
                             fields='TOTMKTCAP, TOTAL_SHARE', df=True)
    fund2.index = fund2.symbol

    # 合并fund1、fund2
    fundamental = pd.concat([fund1, fund2], axis=1, join='inner')

    # 计算价值：价值＝ 基本每股收益 ×（8.5+预期年增长率×2）
    fundamental['value'] = fundamental['BASICEPS'] * (8.5 + context.rate * 2)

    # 计算h_value：h_value = 总股本*价值*0.6 - 总市值，并按值从大到小排序
    fundamental['h_value'] = fundamental['TOTAL_SHARE'] * fundamental['value']*(1 - 0.4) - fundamental['TOTMKTCAP']
    fundamental.sort_values(by='h_value', ascending=False, inplace=True)
    # 若h_value大于总市值则买进，否则就卖出，取前30只股票进行买入
    symbols = fundamental[(fundamental['h_value'])>0].index
    symbols = symbols[:context.num]
    print('股票池标的：', symbols)

    # 获取当前所有仓位
    positions = context.account().positions()

    # 平不在股票池的仓位
    for position in positions:
        symbol = position['symbol']
        if symbol not in symbols:
            order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,
                                 position_side=PositionSide_Long)
            print('市价单平不在股票池的仓位', symbol)

    # 计算每只股票买入比例
    percent = context.ratio / len(symbols)

    # 将股票池中的股票持仓调整至percent
    for symbol in symbols:
        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,
                             position_side=PositionSide_Long)
        print('以市价单调仓至买入比例', symbol)


if __name__ == '__main__':
    '''
        strategy_id策略ID, 由系统生成
        filename文件名, 请与本文件名保持一致
        mode运行模式, 实时模式:MODE_LIVE回测模式:MODE_BACKTEST
        token绑定计算机的ID, 可在系统设置-密钥管理中生成
        backtest_start_time回测开始时间
        backtest_end_time回测结束时间
        backtest_adjust股票复权方式, 不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
        backtest_initial_cash回测初始资金
        backtest_commission_ratio回测佣金比例
        backtest_slippage_ratio回测滑点比例
        '''
    run(strategy_id='432d60a2-10a5-11ed-a6ef-7c10c9b8dfd6',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='9dc704d3ed377bc76c94e882baea6816e93eff27',
        backtest_start_time='2020-11-01 08:00:00',
        backtest_end_time='2020-11-10 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

